/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package server.server;

import protocolHandler.HeaderCodes;
import protocolHandler.ProtoMessageCreator;
import server.closeClientConnections.CloseClientConnectionsProducer;
import server.closeClientConnections.CloseClientConnectionsListener;
import server.closeClientConnections.CloseClientConnectionsEvent;
import protocolHandler.ProtoMessageParser;
import server.server.senderEvent.SenderEvent;
import server.server.senderEvent.SenderListener;
import server.server.senderEvent.SenderProducer;
import server.serverDB.NoSuchAccauntException;
import server.serverDB.ServerDB;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import socketHandler.MessageSocketHandler;

/**
 *
 * @author Александр Емельянов (mr.lex91@gmail.com)
 */
class ClientHandler extends Thread{
    private boolean isRun=false;
    private boolean wasRunnable=false;
    private boolean logginedIn=false;
    private String nickName;
    private Object sync=new Object();
    private MessageSocketHandler socketHandler;
    private CloseClientConnectionsProducer evConnectionProd;
    private CloseClientConnectionsListener evConnectionListener;
    private SenderProducer evSenderProd;
    private SenderListener evSenderListener;
    private ServerDB dataBase;

    ClientHandler(Socket client, CloseClientConnectionsProducer evProd, SenderProducer evSenderProd, ServerDB dataBase)
    {
        try {
            socketHandler=new MessageSocketHandler(client);
        } catch (UnknownHostException ex) {} catch (IOException ex) {}
        this.evConnectionProd=evProd;
        this.dataBase=dataBase;
        this.evSenderProd=evSenderProd;
    }
    public void Start()
    {
        synchronized(sync)
        {
            if (!wasRunnable)
            {
                setDaemon(true);
                setPriority(NORM_PRIORITY);
                isRun=true;
                start();
            }
        }
    }
    private boolean isRunnable()
    {
        synchronized(sync)
        {
            return isRun;
        }
    }

    private void CreateAcc(ProtoMessageParser parser) throws IOException
    {
        if (dataBase.CreateAcc(parser.getHeaderVal("NickName"), parser.getHeaderVal("Password")))
            socketHandler.Write("SucCreateAcc");
        else socketHandler.Write("AccExists");
    }
    private void DeleteAcc(ProtoMessageParser parser) throws IOException
    {
        try {
            if (dataBase.DeleteAcc(parser.getHeaderVal("NickName"), parser.getHeaderVal("Password")))
            {
                socketHandler.Write("SucDeleteAcc");
                evConnectionProd.LogOut(parser.getHeaderVal("NickName"));
            }
            else socketHandler.Write("LogInErr");
        } catch (NoSuchAccauntException ex) {
            socketHandler.Write("AccNotExists");
        }
    }
    @Override
    public void run() {
        ProtoMessageParser parser=new ProtoMessageParser();
        String r;
        String readed="";
        while(true)
        {
            try {
                if (readed.length()==0) r = socketHandler.Read();
                else
                {
                    r=readed;
                    readed="";
                }
                if (!isRunnable())
                {
                    socketHandler.Close();
                    return;
                }
                if (r.length()==0) throw new IOException();
                parser.Parse(r);
                switch(HeaderCodes.getRequestHeaderCode(parser.getType()))
                {
                    case 1:
                    {
                        CreateAcc(parser);
                        break;
                    }
                    case 2:
                    {
                        DeleteAcc(parser);
                        break;
                    }
                    case 3:
                    {
                        if (evSenderProd.ConteinsConnection(parser.getHeaderVal("NickName"))) {
                            socketHandler.Write("AccAlreadyAuth");
                            break;
                        }
                        try{
                            if (dataBase.LogIn(parser.getHeaderVal("NickName"), parser.getHeaderVal("Password")))
                            {
                                nickName=parser.getHeaderVal("NickName");
                                evConnectionListener=new CloseClientConnectionsListener(){
                                    private String nickName;
                                    @Override
                                    public void CloseClientConnections(CloseClientConnectionsEvent e) {
                                        try {
                                            if (e.getMsg().equalsIgnoreCase("LogOut"))
                                            {
                                                socketHandler.Write("LogOutServerInit");
                                                logginedIn=false;
                                            }
                                            else
                                            {
                                                synchronized(sync)
                                                {
                                                    isRun=false;
                                                    wasRunnable=true;
                                                }
                                                socketHandler.Write("ConnectionClosed");
                                            }
                                            finalOnLine(true);
                                        } catch (IOException ex) {
                                            finalOnLine(true);
                                        }
                                    }

                                    @Override
                                    public String getNickName() {
                                        return nickName;
                                    }

                                    @Override
                                    public void setNickName(String nickName) {
                                        this.nickName=nickName;
                                    }
                                };
                                evConnectionListener.setNickName(nickName);
                                evConnectionProd.addClientConnectionsListener(evConnectionListener);
                                evSenderListener=new SenderListener(){
                                    @Override
                                    public void Send(SenderEvent e) {
                                        try {
                                            socketHandler.Write(e.getMsg());
                                        } catch (IOException ex) {
                                            finalOnLine(false);
                                        }
                                    }
                                    @Override
                                    public String getNickName() {
                                        return nickName;
                                    }
                                };
                                evSenderProd.addSenderListener(evSenderListener);
                                evSenderProd.Send("UserOnLine\nAddress: "+nickName);
                                logginedIn=true;
                                socketHandler.Write("SucLogIn");
                                evSenderProd.SendRosterTo(parser.getHeaderVal("NickName"));
                            }
                            else socketHandler.Write("LogInErr");
                            break;
                        } catch (NoSuchAccauntException ex) {
                            socketHandler.Write("AccNotExists");
                        }
                    }
                }
                parser.clear();
                if (logginedIn)
                    while (logginedIn)
                    {
                        r = socketHandler.Read();
                        if (!logginedIn)
                        {
                            evSenderProd.Send("UserOffLine\nAddress: "+nickName);
                            readed=r;
                            break;
                        }
                        if (!isRunnable())
                        {
                            finalOnLine(true);
                            return;
                        }
                        if (r.length()==0) throw new IOException();
                        parser.Parse(r);
                        switch(HeaderCodes.getRequestHeaderCode(parser.getType()))
                        {
                            case 1:
                            {
                                CreateAcc(parser);
                                break;
                            }
                            case 2:
                            {
                                DeleteAcc(parser);
                                break;
                            }
                            case 4:
                            {
                                finalOnLine(true);
                                break;
                            }
                            case 5:
                            {
                                parser.setType("Message");
                                evSenderProd.Send(ProtoMessageCreator.Create(parser));
                                break;
                            }
                            case 6:
                            {
                                parser.setType("MsgFrom");
                                String address=parser.getHeaderVal("Address");
                                parser.setHeader("Address", nickName);
                                evSenderProd.SendTo(ProtoMessageCreator.Create(parser), address);
                                break;
                            }
                            case 7:break;
                            case 8:
                            {
                                evSenderProd.SendRosterTo(nickName);
                                break;
                            }
                            case 10:
                            {
                                String address=parser.getHeaderVal("Address");
                                parser.setHeader("Address", nickName);
                                parser.setType("ReceiveFile");
                                evSenderProd.SendTo(ProtoMessageCreator.Create(parser), address);
                                break;
                            }
                        }
                        parser.clear();
                        if (!logginedIn)
                        {
                            break;
                        }
                        if (!isRunnable())
                        {
                            finalOnLine(logginedIn);
                            socketHandler.Close();
                            return;
                        }
                    }
            } catch (IOException ex) {
                finalOnLine(logginedIn);
                socketHandler.Close();
                return;
            }
        }
    }
    public void finalOnLine(boolean b)
    {
        evConnectionProd.removeCloseClientConnectionsListener(nickName);
        evSenderProd.removeSenderListener(nickName);
        if (b) evSenderProd.Send("UserOffLine\nAddress: "+nickName);
        logginedIn=false;
    }
}
